package com.ymx.darling.util;

import com.ymx.darling.bean.entity.BeanInfo;
import com.ymx.darling.bean.entity.BeanInfoManager;
import com.ymx.darling.bean.entity.FieldInfo;
import com.ymx.darling.exception.DarlingException;
import com.ymx.darling.exception.EntityException;
import com.ymx.darling.exception.ExceptionWrapper;
import com.ymx.darling.mapper.CallableMapper;
import com.ymx.darling.mapper.EntityMapper;
import com.ymx.darling.mapper.SqlMapper;

import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 反射工具类
 * @author 爱Java的小于
 */
public class ReflectUtil {
    private static final Map<String, Method> methodCache = new ConcurrentHashMap<>();
    private static final Map<EntityMapperMethodInfo, Method> entityMapperMethodCache = new HashMap<>();

    static {
        // 缓存EntityMapper的方法
        cacheExtendMapperMethod(EntityMapper.class);
        // 缓存SqlMapper中的方法
        cacheExtendMapperMethod(SqlMapper.class);
        // 缓存CallableMapper中的方法
        cacheExtendMapperMethod(CallableMapper.class);
    }

    /**
     * 缓存指定接口中的方法
     * @param extendMapperClass 用户扩展的Mapper的Class实例
     */
    private static void cacheExtendMapperMethod(Class<?> extendMapperClass) {
        for (Method method : extendMapperClass.getMethods()) {
            EntityMapperMethodInfo methodInfo = EntityMapperMethodInfo.build(method.getName(), method.getParameterTypes());
            entityMapperMethodCache.put(methodInfo, method);
        }
    }


    /**
     * 判断是否是扩展Mapper的方法
     * @param customMapperMethod method
     * @return boolean
     */
    public static boolean isExtendsMethod(Method customMapperMethod) {
        return getExtendsMethod(customMapperMethod) != null;
    }


    /**
     * 返回扩展Mapper中的方法
     * @param customMapperMethod method
     * @return Method
     */
    public static Method getExtendsMethod(Method customMapperMethod) {
        EntityMapperMethodInfo methodKey = EntityMapperMethodInfo.build(customMapperMethod.getName(), customMapperMethod.getParameterTypes());
        return entityMapperMethodCache.get(methodKey);
    }


    /**
     * 返回指定类型的method实例
     * @param entityClass class
     * @param methodOrPropertyName 优先根据方法名查找找不到则根据get字段名的方式查找此方法
     * @return Method
     * @throws NoSuchMethodException 找不到方法时抛出此异常
     */
    public static Method getMethod(Class<?> entityClass, String methodOrPropertyName) throws NoSuchMethodException{
        String methodName = entityClass.getName() + "." + methodOrPropertyName;
        if (!methodCache.containsKey(methodName)) {
            Method method;
            // 先根据名称进行查找
            try {
                method = entityClass.getMethod(methodOrPropertyName);
            } catch (NoSuchMethodException exception) {
                if (methodOrPropertyName.startsWith("get")) {
                    throw new NoSuchMethodException(String.format("类'%s'找不到方法%s", entityClass.getName(), methodOrPropertyName));
                } else {
                    // 查找对应的get方法
                    String getterMethodName = StringUtil.generateGetterMethodName(methodOrPropertyName);
                    try {
                        method = entityClass.getMethod(getterMethodName);
                    } catch (NoSuchMethodException ignored) {
                        throw new NoSuchMethodException(String.format("类'%s'找不到方法%s或%s", entityClass.getName(), methodOrPropertyName, getterMethodName));
                    }
                }
            }
            methodCache.put(methodName, method);
        }
        return methodCache.get(methodName);
    }


    /**
     * 反射调用方法
     * @param method method 被访问实体的Method实例
     * @param object object 被访问实体
     * @param params 参数信息
     * @return Object 返回值
     */
    public static Object invoke(Method method, Object object, Object... params) {
        try {
            return method.invoke(object, params);
        } catch (IllegalAccessException | InvocationTargetException e) {
            throw new EntityException(e, e.getMessage());
        }
    }


    /**
     * 根据属性名称读取实体类的值
     * @param property 属性名
     * @param entity 实体类只能是{@code @Entity}注解的类型
     * @return Object
     */
    public static FieldInfo getColumnInfo(String property, Object entity) throws NoSuchFieldException {
        Objects.requireNonNull(entity);
        Class<?> entityClass = entity.getClass();
        BeanInfo beanInfo = BeanInfoManager.getBeanInfo(entityClass);
        FieldInfo fieldInfo = beanInfo.getFieldMap().get(property);
        if (Objects.isNull(fieldInfo)) {
            throw new NoSuchFieldException("类: '"+entityClass+"' 没有属性: '"+property+"' ");
        }
        return fieldInfo;
    }


    /**
     * 为实体赋值
     * @param field 字段Field实例
     * @param value 值
     * @param entity 实体
     */
    public static void setValue(Field field, Object entity, Object value) {
        try {
            field.set(entity, value);
        } catch (Exception e) {
            if (e instanceof IllegalArgumentException) {
                String valueType = value == null ? "null" : value.getClass().getName();
                throw new EntityException(String.format("不能将'%s'类型的值'%s'赋值给字段'%s'.异常详情:%s:%s", valueType, value, field.getName(), e.getClass().getName(), e.getMessage()));
            } else {
                throw new ExceptionWrapper(e, e.getMessage());
            }
        }
    }


    /**
     * 返回指定属性值
     * @param field 字段Field实例
     * @param entity 实例
     * @return Object
     */
    public static <T> T getValue(Field field, Object entity) {
        try {
            return (T)field.get(entity);
        } catch (Exception e) {
            throw new ExceptionWrapper(e, e.getMessage());
        }
    }


    /**
     * 实例化实体类
     * @param module 实体类class
     * @return T
     * @param <T> T
     */
    public static <T> T newInstance(Class<?> module) {
        try {
            Constructor<?> constructor = module.getConstructor();
            return (T)constructor.newInstance();
        } catch (Exception exception) {
            throw new ExceptionWrapper(exception, exception.getMessage());
        }
    }


    /**
     * 实例化List
     * @param listClass list的class
     * @return list
     * @param <T> T
     */
    public static <T> List<T> newInstanceList(Class<?> listClass) {
        if (List.class.equals(listClass) ||  ArrayList.class.equals(listClass)) {
            return new ArrayList<>();
        } else {
            try {
                Constructor<?> constructor = listClass.getConstructor();
                return (List<T>)constructor.newInstance();
            } catch (Exception exception) {
                throw new ExceptionWrapper(exception, exception.getMessage());
            }
        }
    }


    /**
     * 实例化Map
     * @param mapClass map的class
     * @return map
     * @param <K> K
     * @param <V> V
     */
    public static <K,V> Map<K,V> newInstanceMap(Class<?> mapClass) {
        if (Map.class.equals(mapClass) ||  HashMap.class.equals(mapClass)) {
            return new HashMap<>();
        } else {
            try {
                Constructor<?> constructor = mapClass.getConstructor();
                return (Map<K, V>) constructor.newInstance();
            } catch (Exception exception) {
                throw new ExceptionWrapper(exception, exception.getMessage());
            }
        }
    }


    /**
     * Field 集合类型中的泛型参数类型
     * @param field field
     * @return 当集合类型包含参数类型返回该参数类型 否则 返回null
     */
    public static Class<?> getGenericTypeOfCollectionField(Field field) {
        Type fieldType = field.getGenericType();
        if (fieldType instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) fieldType;
            Type[] typeArguments = parameterizedType.getActualTypeArguments();
            if (typeArguments.length > 0) {
                return (Class<?>) typeArguments[0];
            }
        }
        return null;
    }


    /**
     * 返回指定类型的所有父类
     * @param typeClass class
     * @return 根据继承关系返回多个父类 不继承任何类型时返回空集合
     */
    public static List<Class<?>> getSupperClassArray(Class<?> typeClass) {
        List<Class<?>> supperClassList = new LinkedList<>();
        if (Objects.nonNull(typeClass) && !typeClass.isInterface()) {
            Class<?> type = typeClass;
            while (type != Object.class) {
                type = type.getSuperclass();
                supperClassList.add(type);
            }
        } else {
            throw new EntityException("类'"+typeClass.getName()+"不具备继承性");
        }
        return supperClassList;
    }


    /**
     * 返回指定类型的父类型上的泛型参数类型
     * 父类型的泛型参数只能有一个否则会抛出异常
     * @param type 子类型
     * @param superType 父类型
     * @return 父类型的参数类型
     */
    public static Class<?> getGenericTypeOfSupperType(Class<?> type, Class<?> superType) {
        int superClassIndex = -1;
        Class<?>[] interfaces = type.getInterfaces();
        for (int i = 0; i < interfaces.length; i++) {
            if (interfaces[i].equals(superType)) {
                superClassIndex = i;
                break;
            }
        }

        //判断自定义子类没有继承父类 直接返回null
        if (superClassIndex == -1) {
            return null;
        } else { //获取EntityMapper参数类型
            Type[] genericInterfaces = type.getGenericInterfaces();
            Type genericInterface = genericInterfaces[superClassIndex];
            if (!(genericInterface instanceof ParameterizedType)) {
                return null;
            } else {
                // 返回参数类型
                return getaClass((ParameterizedType) genericInterface, superClassIndex);
            }
        }
    }


    private static Class<?> getaClass(ParameterizedType genericInterface, int superClass) {
        Type[] actualTypeArguments = genericInterface.getActualTypeArguments();
        if (actualTypeArguments.length > 1) {
            throw new EntityException(String.format("类'%s'只能包含一个泛型参数", superClass));
        }
        // 获取参数类型
        Type paramType = actualTypeArguments[0];
        Class<?> entityType;
        // 判断参数类型也包含泛型参数
        if (paramType instanceof  ParameterizedType) {
            entityType = (Class<?>) ((ParameterizedType)paramType).getRawType();
        } else {
            entityType = (Class<?>) paramType;
        }
        return entityType;
    }


    /**
     * 将一个字符串数据转换为指定的基本类型数据
     * @param basicType 基本类型包括String
     * @param stringValue 被转换的字符串数据
     * @return Object
     */
    public static Object convertBasicType(Class<?> basicType, String stringValue) {
        Objects.requireNonNull(basicType, "待转化的类型不能为null类型");
        if (String.class.equals(basicType)) {
            return stringValue;
        } else if (Byte.class.equals(basicType) || Byte.TYPE.equals(basicType)) {
            return Byte.valueOf(stringValue);
        } else if (Short.class.equals(basicType) || Short.TYPE.equals(basicType)) {
            return Short.valueOf(stringValue);
        } else if (Integer.class.equals(basicType) || Integer.TYPE.equals(basicType)) {
            return Integer.valueOf(stringValue);
        } else if (Long.class.equals(basicType) || Long.TYPE.equals(basicType)) {
            return Long.valueOf(stringValue);
        } else if (Float.class.equals(basicType) || Float.TYPE.equals(basicType)) {
            return Float.valueOf(stringValue);
        } else if (Double.class.equals(basicType) || Double.TYPE.equals(basicType)) {
            return Double.valueOf(stringValue);
        } else if (Boolean.class.equals(basicType) || Boolean.TYPE.equals(basicType)) {
            return Boolean.valueOf(stringValue);
        } else {
            throw new DarlingException("不能将值 "+stringValue+" 转换为"+basicType.getName()+"类型");
        }
    }


    private static class EntityMapperMethodInfo {
        private final String methodName;
        private final Class<?>[] parameterTypes;

        private static EntityMapperMethodInfo build(String methodName, Class<?>[] parameterTypes) {
            return new EntityMapperMethodInfo(methodName, parameterTypes);
        }

        private EntityMapperMethodInfo(String methodName, Class<?>[] parameterTypes) {
            this.methodName = methodName;
            this.parameterTypes = parameterTypes;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            EntityMapperMethodInfo that = (EntityMapperMethodInfo) o;
            return Objects.equals(methodName, that.methodName) && Objects.deepEquals(parameterTypes, that.parameterTypes);
        }

        @Override
        public int hashCode() {
            return Objects.hash(methodName, Arrays.hashCode(parameterTypes));
        }
    }
}